;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;						;
;	WSPATCH.ASM				;
;						;
;	Patch for Wordstar 3.0 running under	;
;	The Echelon 'Z' System.  Modifies	;
;	Wordstar to use the current terminal	;
;	characteristics defined in Z3TCAP.	;
;						;
;	Copyright (c) 1985 by Dennis Wright	;
;	and Echelon, Inc.			;
;						;
;	Contains selected routines from 	;
;	ZCPR3's VLIB.				;
;						;
;	VLIB is Copyrighted by Richard Conn	;
;	and Echelon Inc.			;
;						;
;	Note: It may be possable to install	;
;	this patch on other versions of Word-	;
;	Star if the equates below are changed	;
;	to agree with versions' addresses.	;
;						;
;	    WordStar is a trademark of		;
;	MicroPro International Corporation	;
;						;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
	MACLIB	Z3BASE
;
;	WordStar 3.0 USER1 Patch Addresses from Installation manual.
;
TNAME	EQU	0190H		; Terminal name (not documented in manual).
HITE	EQU	0248H		; Number of lines.
WID	EQU	0249H		; Number of columns.
ERAEOL	EQU	026DH		; Erase to end of line.
LINDEL	EQU	0274H		; Delete line.
LININS	EQU	027BH		; Insert line.
IVON	EQU	0284H		; Highlighting on.
IVOFF	EQU	028BH		; Highlighting off.
DELMIS	EQU	02AFH		; Delay for non-cursor set functions.
TRMINI	EQU	0292H		; Terminal init string.
TRMUNI	EQU	029BH		; Terminal de-init string.
UCRPOS	EQU	0264H		; User cursor positioning routine.
INISUB	EQU	02A4H		; User init routine.
MORPAT	EQU	02E0H		; WordStar user patch area.
PBGMEM	EQU	035CH		; Contains beginning of text buffer address.
OCHAR	EQU	0267H		; Contains address of WordStar outchr routine.
;
;	The following equate is the base address of the patch routines.
;
PATCH	EQU	7849H		; Get this address from original PBGMEM.
;
	ORG	UCRPOS		; User-patchable cursor positioning routine.
	JMP	XYPOS		; Use VLIB cursor positioning routine.
;
	ORG	INISUB		; User-patchable init subroutine.
	JMP	INIT		; Use patch init routine.
;
	ORG	MORPAT		; WordStar user patch area.
INIT:	LXI	H,Z3ENV		; Point to 'Z' System environment descriptors.
	LXI	D,80H
	SHLD	ENVPTR		; Save pointer.
	PUSH	H		; Save for later.
	DAD	D		; Displace to Z3TCAP.
	SHLD	VIDPTR		; Save pointer.
	LHLD	OCHAR		; Address of WS outchr routine is kept here.
	SHLD	COUT1+1		; Inline modify patch cout to use WS outchr.
	POP	H		; Restore env pointer.
	LXI	D,47		; Offset to Z3TCAP crt select byte.
	DAD	D
	MOV	A,M		; Get it.
	LXI	D,2		; Set offset to crt 0 data area.
	ORA	A		; Crt 0?
	JZ	GETCRT		; Yes move crt 0 data to WS patch area.
	LXI	D,5		; No, move crt 1 data.
GETCRT:	DAD	D		; Offset to selected Z3TCAP crt data area.
	MOV	A,M		; Get Z3TCAPs terminal width.
	STA	WID		; Patch WS width.
	INX	H		; Bump to Z3TCAPs number of lines.
	MOV	A,M		; Get number of lines.
	STA	HITE		; Patch WS hite.
	XRA	A		; Clear following WS patchs.
	STA	LINDEL		; Line delete
	STA	LININS		; ..and line insert (we don't support these).
	LHLD	VIDPTR		; Get Z3TCAP pointer.
	LXI	D,22		; Offset to CE delay.
	DAD	D
	MOV	A,M		; Get CE delay.
	STA	DELMIS		; Patch WS misc delay.
	INX	H
	CALL	VIDSKP		; Skip past CL string.
	CALL	VIDSKP		; Skip past CM string.
	LXI	D,ERAEOL	; Point to WS eol string.
	CALL	FILL		; Replace with Z3TCAPs eol string.
	LXI	D,IVON		; Point to WS turn on highlighting.
	CALL	FILL		; Replace with Z3TCAPs string.
	LXI	D,IVOFF		; Point to WS turn off highlighting.
	CALL	FILL		; Replace with Z3TCAPs string.
	LXI	D,TRMINI	; Point to WS terminal init string.
	CALL	FILL		; Replace with Z3TCAPs string.
	LXI	D,TRMUNI	; Point to WS terminal de-init string.
	CALL	FILL		; Replace with Z3TCAPs string.
	CALL	NAMIT		; Replace WS terminal name with Z3TCAPs.
	RET			; We're done.
;
	ORG	PBGMEM		; The beginning of the text memory is
	DW	BEGMEM		; ..moved up to accommidate our patch.
;
	ORG	PATCH		; Extended patch area.
NAMIT:	LHLD	VIDPTR		; Point to Z3TCAPs terminal name
	LXI	D,TNAME		; ..and WS terminal name.
	MVI	C,16		; Move our 16 bytes.
NFILL:	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCR	C
	JNZ	NFILL
	XCHG
NFILL2:	MOV	A,M		; Pad out the remainder with spaces.
	CPI	0FH		; Till this.
	RZ
	ORA	A		; Or zero.
	RZ
	MVI	A,20H
	MOV	M,A
	INX	H
	JMP	NFILL2
;
;	This routine moves Z3TCAP data into the WordStar patch area.
;	Because WordStar requires the number of bytes for each entry
;	we must count the bytes we patch and place the number at the
;	beginning of the patch.
;
FILL:	PUSH	D
	MVI	C,-1		; Set byte counter.
FIL1:	INX	D
	MOV	A,M		; Replace WS data with Z3TCAPs.
	STAX	D
	INX	H
	INR	C
	ORA	A		; Reached end?
	JNZ	FIL1
	POP	D		; Yes, restore pointer to first byte.
	MOV	A,C		; Insert number of bytes in string.
	STAX	D
	RET
;
COUT:	PUSH	H
	PUSH	D
COUT1:	CALL	0000H		; This is inline modified by the INIT.
	POP	D		; Routine to call WordStars outchr
	POP	H		; ..routine.
	RET
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;	The following routines are from VLIB by Richard Conn.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; GOTO XY	(From VLIB)
;	HL = Row/Col, with Home=0/0
;	Return with A=0 and Zero Flag Set if not done
;
XYPOS:	MOV	D,L		; Swap row & column
	MOV	E,H		; ..for vlib
	XCHG
GOTOXY:	PUSH	B		; Save regs
	PUSH	D
	PUSH	H
	LHLD	VIDPTR		; Pt to environment
	MOV	A,M		; No terminal?
	CPI	' '+1
	JC	ERR
	LXI	D,15H		; Pt to CM delay
	DAD	D
	MOV	A,M		; Get it
	STA	CMDELAY		; Save it
	INX	H		; Pt to CL string
	INX	H
	CALL	VIDSKP		; Skip CL string
	MOV	A,M		; Get first char of CM string
	ORA	A		; If no string, error
	JZ	ERR
	XCHG			; DE=address of CM string
	POP	H		; Get coordinates in HL
	PUSH	H
	CALL	GXY		; Output xy string with delay
	LDA	CMDELAY		; Pause
	CALL	VIDELAY
	POP	H		; Done
	POP	D
	POP	B
	XRA	A		; Return NZ
	DCR	A
	RET
ERR:	POP	H		; Done
	POP	D
	POP	B
	XRA	A		; Return Z
	RET
;
; GOTOXY	(From VLIB)
;   On input, H=Row and L=Column to Position To (0,0 is Home)
;   On input, DE=address of CM string
;
GXY:	XRA	A		; Set row/column
	STA	RCORDER		; Row before column
	STA	RCBASE		; Add 0 to base
;
; Cycle thru string
;
GXYLOOP:LDAX	D		; Get next char
	INX	D		; Pt to next
	ORA	A		; Done?
	RZ
	CPI	'%'		; Command?
	JZ	GXYCMD
	CPI	'\'		; Escape?
	JZ	GXYESC
	CALL	COUT		; Send char
	JMP	GXYLOOP

;
; Escape - output following byte literally
;
GXYESC:	LDAX	D		; Get next char
	CALL	COUT		; Output literally
	INX	D		; Pt to next
	JMP	GXYLOOP
;
; Interpret next character as a command character
;
GXYCMD:	LDAX	D		; Get command char
	INX	D		; Pt to next
	CPI	'd'		; %d
	JZ	GXYOUT1
	CPI	'2'		; %2
	JZ	GXYOUT2
	CPI	'3'		; %3
	JZ	GXYOUT3
	CPI	'.'		; %.
	JZ	GXYOUT4
	CPI	'+'		; %+v
	JZ	GXYOUT5
	CPI	'>'		; %>xy
	JZ	GXYGT
	CPI	'r'		; %r
	JZ	GXYREV
	CPI	'i'		; %i
	JZ	GXYINC
	CALL	COUT		; Output char if nothing else
	JMP	GXYLOOP
;
; Set row/col home to 0,0
;
GXYINC:	MVI	A,1		; Set rcbase to 1
	STA	RCBASE
	JMP	GXYLOOP
;
; Reverse order of output to column then row (default is row then column)
;
GXYREV:	MVI	A,1		; Set column and row order
	STA	RCORDER
	JMP	GXYLOOP
;
; Command: >xy
;   If value of row/col is greater than x, add y to it
;
GXYGT:	CALL	GETVAL		; Get value
	MOV	C,A		; Save value
	LDAX	D		; Get value to test
	INX	D		; Pt to next
	CMP	C		; If carry, value>x
	JNC	GXYGT1
	LDAX	D		; Get value to add
	ADD	C
	CALL	PUTVAL		; Put value back
GXYGT1:	INX	D		; Pt to next
	JMP	GXYLOOP		; Resume
;
; Command: +n
;   Add n to next value and output
;
GXYOUT5:LDAX	D		; Get value to add
	INX	D		; Pt to next
	MOV	B,A		; Save in B
	CALL	GETVAL		; Get value
	ADD	B		; Add in B
	CALL	COUT		; Output value
RCMARK:	LDA	RCORDER		; Mark output
	ORI	80H
	STA	RCORDER
	JMP	GXYLOOP
;
; Command: .
;   Output next value
;
GXYOUT4:CALL	GETVAL		; Get value
	CALL	COUT		; Output value
	JMP	RCMARK
;
; Command: 3
;   Output next value as 3 decimal digits
;
GXYOUT3:CALL	GETVAL		; Get value
	MVI	B,100		; Output 100's
	MVI	C,1		; Leading zeroes
	CALL	DIGOUT
GXYOT3:	MVI	B,10		; Output 10's
	MVI	C,1		; Leading zeroes
GXYOT2:	CALL	DIGOUT
	ADI	'0'		; Output 1's
	CALL	COUT
	JMP	RCMARK
;
; Command: 2
;   Output next value as 2 decimal digits
;
GXYOUT2:CALL	GETVAL		; Get value
	JMP	GXYOT3
;
; Command: d
;   Output next value as n decimal digits with no leading zeroes
;
GXYOUT1:CALL	GETVAL		; Get value
	MVI	B,100		; Output 100's
	MVI	C,0		; No leading zeroes
	CALL	DIGOUT
	MVI	B,10		; Output 10's
	MVI	C,0		; No leading zeroes
	JMP	GXYOT2
;
; Return next value in A
;
GETVAL:	LDA	RCORDER		; Get order flag
	ORA	A		; Already output the first value?
	JM	GETVAL2
	ANI	1		; Look at lsb
	JZ	GETVALR		; If 0, row first
GETVALC:LDA	RCBASE		; Get base offset
	ADD	L		; Get column
	RET
;
GETVALR:LDA	RCBASE		; Get base offset
	ADD	H		; Get row
	RET
;
GETVAL2:ANI	1		; Look at lsb
	JZ	GETVALC
	JMP	GETVALR
;
; Store A as next value
;
PUTVAL:	MOV	C,A		; Save value
	LDA	RCORDER		; Get order flag
	ORA	A		; Already output the first value?
	JM	PUTVAL2
	ANI	1		; Look at lsb
	JZ	PUTVALR		; If 0, row first
PUTVALC:MOV	L,C		; Set column
	RET
;
PUTVALR:MOV	H,C		; Set row
	RET
;
PUTVAL2:ANI	1		; Look at lsb
	JZ	PUTVALC
	JMP	PUTVALR
;
; Output A as decimal digit char
;   B=Quantity to Subtract from A, C=0 if no leading zero
;
DIGOUT:	PUSH	D		; Save DE
	MVI	D,'0'		; Char
DECOT1:	SUB	B		; Subtract
	JC	DECOT2
	INR	D		; Increment char
	JMP	DECOT1
;
DECOT2:	ADD	B		; Add back in
	PUSH	PSW		; Save result
	MOV	A,D		; Get digit
	CPI	'0'		; Zero?
	JNZ	DECOT3
	MOV	A,C		; Get zero flag
	ORA	A		; 0=no zero
	JZ	DECOT4
DECOT3:	MOV	A,D		; Get digit
	CALL	COUT		; Print it
DECOT4:	POP	PSW		; Get A
	POP	D		; Restore DE
	RET
;
; GXY Buffers
;
RCORDER:DS	1		; 0=row/col, else col/row
RCBASE:	DS	1		; 0=org is 0,0, else org is 1,1
CMDELAY:DS	1		; Number of milliseconds to delay for CM
;
;	VIDELAY pauses for the number of milliseconds indicated by the A
; register.  VIDELAY assumes a ZCPR3 environment and uses it to determine
; processor speed.
;
VIDELAY:PUSH	PSW		; Save regs
	PUSH	B
	PUSH	D
	PUSH	H
	MOV	C,A		; Save count in C
	ORA	A		; No delay?
	JZ	DONE
	LHLD	ENVPTR		; Pt to environment
	LXI	D,2BH		; Offset to processor speed
	DAD	D
	MOV	A,M		; Get processor speed
	ORA	A		; Zero?
	JNZ	VID1
	MVI	A,4		; Assume 4 MHz
VID1:	MOV	B,A		; Processor speed in B
VID2:	PUSH	B		; Delay 1 ms
	CALL	DELAY
	POP	B
	DCR	C		; Count down
	JNZ	VID2
DONE:	POP	H		; Restore regs
	POP	D
	POP	B
	POP	PSW
	RET
;
;  Delay 1 ms at Clock speed
;
DELAY:	CALL	DEL1		; Delay 1 ms at 1MHz
	DCR	B		; Count down clock speed
	JNZ	DELAY
	RET
;
;  Delay 1 ms at 1MHz
;
DEL1:	MVI	C,20		; 20 loops of 51 cycles each ~ 1000 cycles
DEL1A:	XTHL			; 18 cycles
	XTHL			; +18 = 36 cycles
	DCR	C		; + 5 = 41 cycles
	JNZ	DEL1A		; +10 = 51 cycles
	RET
;
;  VIDSKP - Skip over video string pted to by HL; pt to byte after string
;
VIDSKP:	MOV	A,M		; Get next char
	INX	H		; Pt to next
	ORA	A		; Done if zero
	RZ
	CPI	'\'		; Literal value?
	JNZ	VIDSKP		; Continue if not
	INX	H		; Pt to after literal value
	JMP	VIDSKP
;
;  ZCPR3 Pointer save area
;
VIDPTR:	DS	2		; First byte of termcap entry
ENVPTR:	DS	2		; 'Z' System environment package.
;
BEGMEM	EQU	$
;
	END
